package jgroups.demos;

import java.awt.Button;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Label;
import java.awt.Rectangle;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.Enumeration;
import java.util.Hashtable;

import org.jgroups.Address;
import org.jgroups.Channel;
import org.jgroups.JChannel;
import org.jgroups.MembershipListener;
import org.jgroups.View;
import org.jgroups.blocks.RequestOptions;
import org.jgroups.blocks.ResponseMode;
import org.jgroups.blocks.RpcDispatcher;
import org.jgroups.util.Rsp;
import org.jgroups.util.RspList;

/**
 * Used in conjunction with QuoteServer: a client is member of a group of quote servers which replicate
 * stock quotes among themselves. The client broadcasts its request (set, get quotes) and (in the case of get
 * waits for the first reply received (usually the one from the quote server closest to it). The client
 * can get and set quotes as long as a minimum of 1 server (in the group) is running.
 * @author Bela Ban
 */
public class QuoteClient extends Frame implements WindowListener,
		ActionListener, MembershipListener {
	static final String channel_name = "Quotes";
	RpcDispatcher disp;
	Channel channel;
	final Button get = new Button("Get");
	final Button set = new Button("Set");
	final Button quit = new Button("Quit");
	final Button get_all = new Button("All");
	final Label stock = new Label("Stock");
	final Label value = new Label("Value");
	final Label err_msg = new Label("Error");
	final TextField stock_field = new TextField();
	final TextField value_field = new TextField();
	final java.awt.List listbox = new java.awt.List();
	final Font default_font = new Font("Helvetica", Font.PLAIN, 12);

	static final String props = null; // default stack from JChannel

	public QuoteClient() {
		super();
		try {
			channel = new JChannel(props);
			channel.setDiscardOwnMessages(true);
			disp = new RpcDispatcher(channel, null, this, this);
			channel.connect(channel_name);
		} catch (Exception e) {
			System.err.println("QuoteClient(): " + e);
		}
		addWindowListener(this);
	}

	private void showMsg(String msg) {
		err_msg.setText(msg);
		err_msg.setVisible(true);
	}

	private void clearMsg() {
		err_msg.setVisible(false);
	}

	public void start() {
		setLayout(null);
		setSize(400, 300);
		setFont(default_font);

		stock.setBounds(new Rectangle(10, 30, 60, 30));
		value.setBounds(new Rectangle(10, 60, 60, 30));
		stock_field.setBounds(new Rectangle(100, 30, 100, 30));
		value_field.setBounds(new Rectangle(100, 60, 100, 30));
		listbox.setBounds(210, 30, 150, 160);
		err_msg.setBounds(new Rectangle(10, 200, 350, 30));
		err_msg.setFont(new Font("Helvetica", Font.ITALIC, 12));
		err_msg.setForeground(Color.red);
		err_msg.setVisible(false);
		get.setBounds(new Rectangle(10, 250, 80, 30));
		set.setBounds(new Rectangle(100, 250, 80, 30));
		quit.setBounds(new Rectangle(190, 250, 80, 30));
		get_all.setBounds(new Rectangle(280, 250, 80, 30));

		get.addActionListener(this);
		set.addActionListener(this);
		quit.addActionListener(this);
		get_all.addActionListener(this);

		add(stock);
		add(value);
		add(stock_field);
		add(value_field);
		add(err_msg);
		add(get);
		add(set);
		add(quit);
		add(get_all);
		add(listbox);
		// stock_field.requestFocus();
		setVisible(true);
	}

	public void windowActivated(WindowEvent e) {
	}

	public void windowClosed(WindowEvent e) {
	}

	public void windowClosing(WindowEvent e) {
		System.exit(0);
	}

	public void windowDeactivated(WindowEvent e) {
	}

	public void windowDeiconified(WindowEvent e) {
	}

	public void windowIconified(WindowEvent e) {
	}

	public void windowOpened(WindowEvent e) {
	}

	public void actionPerformed(ActionEvent e) {
		String command = e.getActionCommand();

		try {
			if (command.equals("Get")) {
				String stock_name = stock_field.getText();
				if (stock_name == null || stock_name.length() == 0) {
					showMsg("Stock name is empty !");
					return;
				}
				showMsg("Looking up value for " + stock_name + ':');
				RspList<Object> quotes = disp.callRemoteMethods(null,
						"getQuote", new Object[] { stock_name },
						new Class[] { String.class }, new RequestOptions(
								ResponseMode.GET_ALL, 10000));

				Float val = null;
				for (Rsp<Object> rsp : quotes.values()) {
					Object quote = rsp.getValue();
					if (quote == null || quote instanceof Throwable)
						continue;
					val = (Float) quote;
					break;
				}

				if (val != null) {
					value_field.setText(val.toString());
					clearMsg();
				} else {
					value_field.setText("");
					showMsg("Value for " + stock_name + " not found");
				}
			} else if (command.equals("Set")) {
				String stock_name = stock_field.getText();
				String stock_val = value_field.getText();
				if (stock_name == null || stock_val == null
						|| stock_name.length() == 0 || stock_val.length() == 0) {
					showMsg("Stock name and value have to be present to enter a new value");
					return;
				}
				Float val = new Float(stock_val);
				disp.callRemoteMethods(null, "setQuote", new Object[] {
						stock_name, val }, new Class[] { String.class,
						Float.class }, new RequestOptions(
						ResponseMode.GET_FIRST, 0));

				showMsg("Stock " + stock_name + " set to " + val);
			} else if (command.equals("All")) {
				listbox.removeAll();
				showMsg("Getting all stocks:");
				RspList<Object> rsp_list = disp.callRemoteMethods(null,
						"getAllStocks", null, null, new RequestOptions(
								ResponseMode.GET_ALL, 5000));

				System.out.println("rsp_list is " + rsp_list);

				Hashtable all_stocks = null;
				for (Rsp rsp : rsp_list.values()) {
					Object obj = rsp.getValue();
					if (obj == null || obj instanceof Throwable)
						continue;
					all_stocks = (Hashtable) obj;
					break;
				}

				if (all_stocks == null) {
					showMsg("No stocks found");
					return;
				}
				clearMsg();
				listbox.removeAll();
				String key;
				Float val;
				for (Enumeration en = all_stocks.keys(); en.hasMoreElements();) {
					key = (String) en.nextElement();
					val = (Float) all_stocks.get(key);
					if (val == null)
						continue;
					listbox.add(key + ": " + val.toString());
				}
			} else if (command.equals("Quit")) {
				setVisible(false);
				channel.close();
				System.exit(0);
			} else
				System.out.println("Unknown action");
		} catch (Exception ex) {
			value_field.setText("");
			ex.printStackTrace();
			showMsg(ex.toString());
		}
	}

	public void setQuote(String stock_name, Float value) {
		;
	}

	public void printAllStocks() {
	}

	public void viewAccepted(View new_view) {
		setTitle("Members in " + channel_name + ": " + (new_view.size() - 1));
	}

	public void suspect(Address suspected_mbr) {
	}

	public void block() {
	}

	public void unblock() {
	}

	public static void main(String args[]) {
		QuoteClient client = new QuoteClient();
		client.start();
	}

}
